﻿@using Gurux.DLMS.AMI.Shared.DIs

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Gurux.DLMS.AMI.Shared.DTOs
@using Gurux.DLMS.AMI.Shared.Rest
@using Gurux.DLMS
@using Gurux.DLMS.ManufacturerSettings
@using Gurux.DLMS.Objects
@using System.Xml.Linq
@using System.IO
@using System.Xml
@using System.Xml.Serialization
@using Gurux.DLMS.AMI.Shared.Enums
@using Gurux.DLMS.AMI.Client.Helpers
@using Gurux.DLMS.AMI.Module
@using System.Text.Json
@using System.Web;
@using System.Text.Json.Serialization;
@using System.Text.Encodings.Web;

@attribute [Authorize(Roles = "Admin, TemplateManager")]
@inject NavigationManager NavigationManager
@inject HttpClient Http
@inject IGXNotifier Notifier
@inject ILogger<DeviceTemplate> Logger

@if (Active != null)
{
    <EditForm Model="@Active">
        <DataAnnotationsValidator />
        <GXValidator @ref="_validator" OnValidate="OnValidate" />
        <ValidationSummary />
        <table>
            <tr>
                <th>
                    @if (isLoading)
                    {
                        <p>加载 @fileName...</p>
                    }
                    else
                    {
                        @if (IsAdding)
                        {
                            <!--FileSelector is disabled until user has selected device template.-->
                            <FileSelector Value="@_value"
                                          Style="width:100%"
                                          MaxFileSize="10000000"
                                          OnLoaded="@UpdateDeviceTemplates"
                                          Filter=".gxc" />
                        }
                        @if (IsAdding && templates != null && templates.Any())
                        {
                        <th>类型:</th>
                        <InputSelect id="type" @bind-Value="TemplateType" placeholder="Device template type">
                                @foreach (var it in templates)
                                {
                                <option value="@it.Type">@it.Type</option>
                                }
                        </InputSelect>
                        }
                    }
                </th>
            </tr>
            <tr>
                <th>
                    名称:
                </th>
                <th>
                    <InputText id="name" disabled="@IsDeleting"
                               @bind-Value="TemplateName"
                               placeholder="Device template name" />
                </th>
            </tr>
            <tr>
                <th>
                    等待时间
                </th>
                <th>
                    <InputNumber id="waitTime" disabled="@IsDeleting"
                                 @bind-Value="Active.WaitTime" />
                </th>
            </tr>
            <tr>
                <th>
                    重发次数
                </th>
                <th>
                    <InputNumber id="resendCount" disabled="@IsDeleting"
                                 @bind-Value="Active.ResendCount" />
                </th>
            </tr>
            <tr>
                <th>
                    @Properties.Resources.Authentication
                </th>
                <th>
                    <InputSelect id="type" class="form-select"
                                 @bind-Value="Authentication" disabled="true">
                        @foreach (var it in GetAuthentications())
                        {
                            <option value="@Convert.ToInt32(@it)">@it</option>
                        }
                    </InputSelect>
                </th>
            </tr>
            <tr>
                <th>
                    <InputHexAscii @ref="@_password"
                                   Disabled="@(IsDeleting || IsPasswordTileDisabled())"
                                   @bind-Value="DevicePassword"
                                   Title="@Properties.Resources.Password">
                    </InputHexAscii>
                    <ValidationMessage For="@(() => _password)" />
                </th>
            </tr>
        </table>
        <TabControl>
            <ChildContent>
                <TabPage Text="配置" Icon="oi oi-data-transfer-download">
                    <div style="position: relative; width: 100%; ">
                        <Profiles Parent="this" @ref="_profiles"></Profiles>
                    </div>
                </TabPage>
                <TabPage Text="加密" Icon="oi oi-key">
                    <div style="position: relative; width: 100%; ">
                        <Ciphering Parent="this" @ref="_ciphering"></Ciphering>
                    </div>
                </TabPage>
                <TabPage Text="高级" Icon="oi oi-cog">
                    <div style="position: relative; width: 100%; ">
                        <Advanced Parent="this" @ref="_advanced"></Advanced>
                    </div>
                </TabPage>
            </ChildContent>
        </TabControl>
        <div class="form-group">
            <label>@Properties.Resources.CreationTime</label>
            <InputDate Type="@InputDateType.DateTimeLocal" id="generation" readonly="readonly" class="form-control"
                       @bind-Value="Active.CreationTime" />
        </div>
        @if (Active.Updated != null)
        {
            <div class="form-group">
                <label>@Properties.Resources.Updated</label>
                <InputDate Type="@InputDateType.DateTimeLocal" id="generation" readonly="readonly" class="form-control"
                           @bind-Value="Active.Updated" />
            </div>
        }
        @if (Active.Removed != null)
        {
            <div class="form-group">
                <label>@Properties.Resources.Remove</label>
                <InputDate Type="@InputDateType.DateTimeLocal" id="remove" class="form-control"
                           @bind-Value="Active.Removed" />
            </div>
        }
    </EditForm>
}
@code {
    private InputHexAscii? _password;
    private GXValidator? _validator;
    private Ciphering? _ciphering;
    private Advanced? _advanced;
    private Profiles? _profiles;

    private string? _value;

    [CascadingParameter]
    private DeviceTemplateTab? Parent { get; set; }
    private GXDeviceTemplate? _active;

    internal GXDeviceTemplate? Active
    {
        get
        {
            if (_active != null)
            {
                return _active;
            }
            return Parent?.Active;
        }
    }

    /// <summary>
    /// User action.
    /// </summary>
    [Parameter]
    public string? Action { get; set; }

    private CrudAction action;
    /// <summary>
    /// Selected item.
    /// </summary>
    [Parameter]
    public Guid? Id { get; set; }

    private bool DeleteTarget;
    private bool isLoading;
    List<GXDeviceTemplate> templates = new List<GXDeviceTemplate>();
    private string? fileName = null;

    public bool IsDeleting
    {
        get
        {
            return action == CrudAction.Delete;
        }
    }

    //User changes name of the Active.
    string? TemplateName
    {
        get
        {
            if (Active == null)
            {
                return null;
            }
            return Active.Name;
        }
        set
        {
            if (Active != null)
            {
                Active.Name = value;
            }
        }
    }

    public bool IsAdding
    {
        get
        {
            return action == CrudAction.Create;
        }
    }

    private string? TemplateType
    {
        get
        {
            return Active?.Type;
        }
        set
        {
            foreach (GXDeviceTemplate it in templates)
            {
                if (Parent != null && it.Type == value)
                {
                    _active = it;
                    break;
                }
            }
        }
    }

    private Guid SelectedSettings { get; set; }

    private List<Gurux.DLMS.Enums.Authentication> GetAuthentications()
    {
        List<Gurux.DLMS.Enums.Authentication> list = new List<Gurux.DLMS.Enums.Authentication>();
        list.Add(Gurux.DLMS.Enums.Authentication.None);
        list.Add(Gurux.DLMS.Enums.Authentication.Low);
        list.Add(Gurux.DLMS.Enums.Authentication.High);
        list.Add(Gurux.DLMS.Enums.Authentication.HighMD5);
        list.Add(Gurux.DLMS.Enums.Authentication.HighSHA1);
        list.Add(Gurux.DLMS.Enums.Authentication.HighGMAC);
        list.Add(Gurux.DLMS.Enums.Authentication.HighSHA256);
        list.Add(Gurux.DLMS.Enums.Authentication.HighECDSA);
        return list;
    }

    internal Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings? Settings;
    private byte Authentication
    {
        get
        {
            if (Settings == null)
            {
                return 0;
            }
            return Settings.Authentication;
        }
        set
        {
            if (Settings != null)
            {
                if (Settings.Authentication != value)
                {
                    Settings.Authentication = value;
                }
            }
        }
    }

    private bool IsPasswordTileDisabled()
    {
        if (Settings != null)
        {
            return (Settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.HighGMAC ||
                Settings.Authentication == (byte)Gurux.DLMS.Enums.Authentication.None);
        }
        return false;
    }

    private byte[]? DevicePassword
    {
        get;
        set;
    }

    /// <summary>
    /// Get device templates from the XML.
    /// </summary>
    /// <param name="xml"></param>
    private void UpdateDeviceTemplates(string? xml)
    {
        try
        {
            Notifier.ClearStatus();
            Notifier.ProgressStart();
            isLoading = true;
            StateHasChanged();
            templates = ClientHelpers.ConvertToTemplates(Logger, xml);
            if (Parent != null && templates.Any())
            {
                _active = templates[0];
            }
        }
        catch (Exception ex)
        {
            fileName = null;
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier.ProgressEnd();
            isLoading = false;
            StateHasChanged();
        }
    }

    /// <summary>
    /// Device template tab asks to update the UI.
    /// </summary>
    public void Update()
    {
        if (Active?.Settings != null)
        {
            Settings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Settings);
            if (Settings != null)
            {
                DevicePassword = GXDLMSTranslator.HexToBytes(Settings?.Password);
                if (DevicePassword != null)
                {
                    DevicePassword = Settings?.HexPassword;
                }
                _profiles?.Update(Settings);
                _ciphering?.Update(Settings);
                _advanced?.Update(Settings);
            }
            StateHasChanged();
        }
    }

    protected override async Task OnInitializedAsync()
    {
        try
        {
            if (Active == null && Id != null)
            {
                Notifier.ProgressStart();
                Notifier.ClearStatus();
                Notifier.Clear();
                try
                {
                    //Get device template data.
                    var tmp = (await Http.GetAsJsonAsync<GetDeviceTemplateResponse>(string.Format("api/DeviceTemplate?id={0}", Id)));
                    if (tmp?.Item != null)
                    {
                        _active = tmp.Item;
                    }
                    else
                    {
                        NavigationManager.NavigateTo("404");
                    }
                }
                finally
                {
                    Notifier?.ProgressEnd();
                }
            }
            if (Active == null)
            {
                throw new ArgumentException(Properties.Resources.InvalidTarget);
            }
            action = ClientHelpers.GetAction(Action);
            if (action != CrudAction.Create)
            {
                templates.Add(Active);
            }

            if (action == CrudAction.Update && Active.Settings != null)
            {
                Settings = JsonSerializer.Deserialize<Gurux.DLMS.AMI.Shared.DTOs.Device.GXDLMSSettings>(Active.Settings);
                if (Settings != null)
                {
                    DevicePassword = Settings.HexPassword;
                    _ciphering?.Update(Settings);
                    _advanced?.Update(Settings);
                }
            }

            if (action == CrudAction.Delete)
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Remove, Icon = "oi oi-trash", OnClick = OnSave });
            }
            else
            {
                Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Save, Icon = "oi oi-pencil", OnClick = OnSave });
            }
            Notifier.AddMenuItem(new GXMenuItem() { Text = Properties.Resources.Cancel, Icon = "oi oi-action-undo", OnClick = OnCancel });
            Notifier.UpdateButtons();
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
    }

    /// <summary>
    /// Validate the device content.
    /// </summary>
    /// <param name="sender"></param>
    private void OnValidate(GXValidator sender)
    {
        if (Active != null)
        {
            if (string.IsNullOrEmpty(Active.Name))
            {
                sender.AddError(() => Active.Name, "Invalid name.");
            }
            if (Active.Objects == null || Active.Objects.Count == 0)
            {
                throw new Exception("Device template file is not selected.");
            }
            if (Settings != null)
            {
                //Check security settings.
                _ciphering?.Validate(sender, Settings);
                _profiles?.Validate(sender, Settings);
                _advanced?.Validate(sender, Settings);
            }
        }
    }

    private void UpdateSettings()
    {
        //Update settings
        if (Active != null && _password != null && Settings != null)
        {
            var options = new JsonSerializerOptions
                {
                    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
                };
            _ciphering?.UpdateSettings(Settings);
            _profiles?.UpdateSettings(Settings);
            _advanced?.UpdateSettings(Settings);
            Settings.Password = null;
            Settings.HexPassword = _password?.GetByteArray();
            //Set empty strings to null.
            @foreach (var it in Settings.GetType().GetProperties())
            {
                object value = it.GetValue(Settings);
                if (value is string str)
                {
                    if (str == string.Empty)
                    {
                        it.SetValue(Settings, null);
                    }
                }
                if (value is byte[] ba)
                {
                    if (ba.Length == 0)
                    {
                        it.SetValue(Settings, null);
                    }
                }
            }
            Active.Settings = JsonSerializer.Serialize(Settings, options);
        }
    }

    /// <summary>
    /// Save device Active.
    /// </summary>
    public async void OnSave()
    {
        try
        {
            if (Active == null)
            {
                throw new Exception(Properties.Resources.InvalidTarget);
            }
            _validator?.ClearErrors();
            if (Active == null)
            {
                throw new Exception(Properties.Resources.InvalidTarget);
            }
            if (_validator != null && !_validator.Validate())
            {
                return;
            }
            Notifier.ProgressStart();
            Notifier.ClearStatus();
            if (action == CrudAction.Delete)
            {
                await Http.PostAsJson<RemoveDeviceTemplateResponse>("api/DeviceTemplate/Delete", new RemoveDeviceTemplate()
                    {
                        Ids = new Guid[] { Active.Id },
                        Delete = DeleteTarget
                    });
            }
            else if (action == CrudAction.Create)
            {
                await Http.PostAsJson<UpdateDeviceTemplateResponse>("api/DeviceTemplate/Add", new UpdateDeviceTemplate()
                    {
                        Templates = new GXDeviceTemplate[] { Active }
                    });
            }
            else if (action == CrudAction.Update)
            {
                UpdateSettings();
                await Http.PostAsJson<UpdateDeviceTemplateResponse>("api/DeviceTemplate/Update", new UpdateDeviceTemplate()
                    {
                        Templates = new GXDeviceTemplate[] { Active }
                    });
            }
            else
            {
                throw new Exception(Properties.Resources.InvalidTarget);
            }
            ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
        }
        catch (AccessTokenNotAvailableException exception)
        {
            exception.Redirect();
        }
        catch (Exception ex)
        {
            Notifier?.ProcessError(ex);
        }
        finally
        {
            Notifier.ProgressEnd();
        }
    }

    /// <summary>
    /// Cancel update.
    /// </summary>
    private void OnCancel()
    {
        ClientHelpers.NavigateToLastPage(NavigationManager, Notifier);
    }
}
